/******************************************************************************
 * Author:      671643387
 * Created:     2016/9/26
 *****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
//>> 导入库
var fs              = require('fs');
var http            = require('http');
var qs              = require('querystring');
var url             = require('url');
var util            = require('util');
var redis           = require('redis');
var WebSocketServer = require('websocket').server;
var logic           = require('./logic');
var mongodb         = require('mongodb');
global.config       = require(require('fs').existsSync('../config.js') ? '../config.js' : './config.js');
var csv             = require('./csv.js');


///////////////////////////////////////////////////////////////////////////////
//>> 功能功函数

/**
 * 加载服务器配置
 * @param name
 */
function loadConf(name) {
    if (name == 'laomazi') {
        global.gConfLaomazi = new csv.CommonCSV('conf/laomazi.dat',['category', 'type', 'value']);
    }
}

/**
 * 连接数据库
 * @param callback
 */
function loadDB(callback) {
    var mongoServer = new mongodb.Server(config.MongodbHost, config.MongodbPort,
        {auto_reconnect: true, poolSize: 4});
    var db = new mongodb.Db(config.MongodbName, mongoServer,
        {'native_parser': false, 'w': 1, 'wtimeout': 10, 'fsync': true});

    db.open(function(err, db) {
        if (err) {
            ERROR(err);
            process.exit(-1);
        }
        callback && callback(db);
    });
}

/**
 * 连接缓存服务器
 * @param callback
 */
function loadCache(callback) {
    var client = redis.createClient(config.RedisPort, config.RedisHost);
    client.select(config.RedisId, function(err) {
        if (err) {
            ERROR(err);
            process.exit(-1);
        }
    });

    client.on('ready', function() {
        callback && callback(client);
    });

    client.on('error', function(e) {
        ERROR(e);
    });
}

/**
 * 启动WEB服务器
 * @param serverName 服务器名
 * @param port       监听端口
 * @param ip         监听地址
 * @param handler    请求处理程序
 * @param onExit     进程退出处理
 */
function startWebServer(serverName, port, ip, handler, onExit) {
    var isExiting = false;
    var server = http.createServer(function(req, res) {
        if (isExiting) {
            req.connection.destroy();
            return;
        }

        var body = '';
        req.on('data', function(chunk) {
            body += chunk;
        });

        req.on('end', function() {
            var rawData = '';
            if (req.method == 'POST') {
                rawData = body;
            } else {
                rawData = url.parse(req.url).query;
            }

            var query = qs.parse(rawData);
            res._query = query;
            res._time = +(new Date());
            res._ip = req.connection.remoteAddress;
            handleReq(query, res, handler);
        });

    });
    server.listen(port, ip, function(){
        INFO(serverName + ' start');
        fs.writeFileSync(pidFile, process.pid, 'utf8');
    });

    var pidFile = serverName + '.pid';
    process.on('SIGINT', beforExit);
    process.on('SIGTERM', beforExit);

    process.on('uncaughtException', function(err) {
        ERROR(err.stack);
        if (err.code == 'EADDRINUSE') {
            beforExit();
        }
    });

    function beforExit() {
        INFO(serverName + ' begin shutdown');
        isExiting = true;

        if (onExit) {
            onExit(endExit);
        } else {
            endExit();
        }
    }

    function endExit() {
        fs.existsSync(pidFile) && fs.unlinkSync(pidFile);
        INFO(serverName + ' end shutdown');

        // delete this
        LOG('end stopping : ' + (new Date())/1000);
        shutdownLog(function() {
            process.exit();
        });
    }

    return server;
}

function startWssServer(serverName, port, ip, handler, wssInit, onExit) {
    var httpServer = startWebServer(serverName, port, ip, handler, onExit);
    var wsServer = new WebSocketServer({
        httpServer: httpServer,
    });
    wssInit(wsServer);
}

function handleReq(query, res, handler) {
    var resp = {
        'code': 0,
        'desc': '',
        'data': {}
    };

    if (query.args) {
        try {
            query.args = JSON.parse(query.args);
        } catch (e) {
            query.args = {};
            ERROR('parse args failed');
        }
    }

    if (query.mod == 'dump' && query.act == 'dump') {
        resp.data = dumpObj(query.path);
        onReqHandled(res, resp);
    } else {
        handler(query, res, resp);
    }
}

global.onReqHandled = function(res, data, noCompress) {
    if (!data.hasOwnProperty('code')) {
        data.code = 1;
        data.desc = 'no code';
    }

    if (!data.hasOwnProperty('desc')) {
        data.desc = '';
    }

    data.serverTime = Date.getStamp();
    var strData = JSON.stringify(data)
    var query = res._query;
    res.end(strData);

    DEBUG(strData);
};

/**
 * 检查连接是否被允许
 * @param origin
 * @param callback
 */
global.checkOriginIsAllowed = function(origin, callback) {
    callback(true);
};

/**
 * 处理连接关闭
 * @param conn
 * @param origin
 */
global.handleConnectionClose = function(conn, origin) {
    LOG('Connection closed  ' + conn.conn_id);

    // 通知玩家断线
    conn.player && conn.player.onDisconnect();
};

/**
 * WSS请求处理完毕
 * @param query
 * @param resp
 */
global.onWsHandled = function(query, resp) {
    var conn = query.conn;

    // 有些请求不需要回复
    if (!resp.nosend) {
        resp.serverTime = Date.getStamp();
        var strData = JSON.stringify(resp)

        conn.sendUTF(strData);
    }

    var timeCost = +(new Date()) - conn._time;
    LOG(util.format('%s %s, %d, %s - %d', query.mod, query.act, resp.code, resp.desc, timeCost));
};

function dumpObj(name) {
    var names = name.split('.');

    var curObj = global;
    for (var key in names) {
        var curName = names[key];
        if (!curObj.hasOwnProperty(curName)) {
            return null;
        }
        curObj = curObj[curName];
    }

    return curObj;
}

global.requestGame = function(uid, args, mod, act, callback) {
    var options = {
        host: config.GameHost,
        port: config.GamePort,
        path: '/',
        method: 'POST'
    };

    var resp = {
        code: 0,
        desc: '',
        data: {},
    };
    var req = http.request(options, function(res) {
        var chunks = [];
        res.on('data', function(chunk) {
            chunks.push(chunk);
        });

        res.on('end', function() {
            var data = Buffer.concat(chunks).toString();
            var gameResp = null;
            try {
                gameResp = JSON.parse(data);
            } catch (error) {
                ERROR('laomazi resp ' + data);
                gameResp = null;
            }

            if (!gameResp) {
                resp.code = 1;
                resp.desc = 'request game error';
            } else {
                resp.code = gameResp.code;
                resp.desc = gameResp.desc;
                resp.data = gameResp.data;
            }

            callback && callback(resp);
        });
    });

    req.on('error', function(err) {
        resp.code = 1;
        resp.desc = 'request game error';
        callback && callback(resp);
    });

    req.end(util.format('mod=%s&act=%s&uid=%d&args=%s', mod, act,
        uid, JSON.stringify(args)));
    req.end();
}

global.httpGet = function(url, callback, json) {
    http.get(url, function(res) {
        var chunks = [];
        res.on('data', function(chunk) {
            chunks.push(chunk);
        });

        res.on('end', function() {
            var data = Buffer.concat(chunks).toString();
            if (json) {
                try {
                    data = JSON.parse(data);
                } catch (e) {
                    data = null;
                }
            }
            callback && callback(data);
        });
    }).on('error', function(e) {
        if (json) {
            callback && callback(null);
        } else {
            callback && callback('error');
        }
    });
};

exports.loadConf = loadConf;
exports.loadDB = loadDB;
exports.loadCache = loadCache;
exports.startWebServer = startWebServer;
exports.startWssServer = startWssServer;